/*
 *    Copyright 2009-2014 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.binding;

import org.apache.ibatis.reflection.ExceptionUtil;
import org.apache.ibatis.session.SqlSession;

import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;

/**
 * @author Clinton Begin
 * @author Eduardo Macarron
 */

/**
 * 映射器代理，代理模式
 */
public class MapperProxy<T> implements InvocationHandler, Serializable {

    private static final long serialVersionUID = -6424540398559729838L;
    /**
     * 记录 关联 SqlSession 对象
     */

    private final SqlSession sqlSession;
    /**
     * Mapper 对应 Class 对象
     */
    private final Class<T> mapperInterface;
    /**
     * 用于缓存 apperMethod 对象，其 key Mapper 口中 对应 Method 对象， value 是对应
     * MapperMethod 对象 MapperMethod 对象会完成参数转换以 SQL 语句的执
     * 需要注意的是， MapperMethod 中并不记录任何状态 关的信息，所以 以在多个代 对象之 共享
     */
    private final Map<Method, MapperMethod> methodCache;

    public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
        this.sqlSession = sqlSession;
        this.mapperInterface = mapperInterface;
        this.methodCache = methodCache;
    }

    /**
     * 代理方法
     * @param proxy  代理对象
     * @param method 代理方法
     * @param args   代理方法的参数
     * @return 方法执行结果
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //代理以后，所有Mapper的方法调用时，都会调用这个invoke方法
        //并不是任何一个方法都需要执行调用代理对象进行执行，如果这个方法是Object中通用的方法（toString、hashCode等）无需执行
        if (Object.class.equals(method.getDeclaringClass())) {
            try {
                return method.invoke(this, args);
            } catch (Throwable t) {
                throw ExceptionUtil.unwrapThrowable(t);
            }
        }
        //从缓存中获取MapperMethod对象,如采缓存中没有,则创建新 MapperMethod 对象 加到缓存中
        //这里优化了，去缓存中找MapperMethod
        final MapperMethod mapperMethod = cachedMapperMethod(method);
        //执行
        //调用 MapperMethod.execute()方法执行 SQL语句
        return mapperMethod.execute(sqlSession, args);
    }

    //去缓存中找MapperMethod
    private MapperMethod cachedMapperMethod(Method method) {
        //在缓存中查找MapperMethod
        MapperMethod mapperMethod = methodCache.get(method);
        if (mapperMethod == null) {
            //找不到才去new
            //创建 MapperMethod 对象 并添加到 methodCache 集合
            mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
            methodCache.put(method, mapperMethod);
        }
        return mapperMethod;
    }

}
